package com.fishleap.sqlSession;

import com.fishleap.pojo.BoundSql;
import com.fishleap.pojo.Configuration;
import com.fishleap.pojo.MapperStatement;
import com.fishleap.utils.GenericTokenParser;
import com.fishleap.utils.ParameterMapping;
import com.fishleap.utils.ParameterMappingTokenHandler;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.List;

/**
 * @author zlg
 * @create 2020-04-28 23:48
 */
public class PreparedStatementHandler {

    private Configuration configuration;

    private MapperStatement mapperStatement;

    private Object[] params;

    public PreparedStatementHandler(Configuration configuration, MapperStatement mapperStatement, Object[] params) {
        this.configuration = configuration;
        this.mapperStatement = mapperStatement;
        this.params = params;
    }

    public PreparedStatement getPreparedStatement() throws Exception{
        // 1.注册驱动,获取连接
        Connection connection = configuration.getDatasource().getConnection();
        // 2.转化sql语句,将 #{id} 转为 `?`,字符,并将id进行存储
        String sql = mapperStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        // 3.获取预处理对象PreparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
        // 4.设置参数
        // 获取类的全路径并生成当前类的字节码对象
        String paramterType = mapperStatement.getParamterType();
        Class<?> paramterTypeClass = getClassType(paramterType);
        // 获取所有存储的sql参数值并根据反射获取对应属性值进行设置参数
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        for (int i = 0; i < parameterMappings.size(); i++) {
            // 获取存储的sql参数,即对象中的某个属性
            ParameterMapping parameterMapping = parameterMappings.get(i);
            String content = parameterMapping.getContent();
            // 通过反射设置参数
            Field declaredField = paramterTypeClass.getDeclaredField(content);
            // 设置访问
            declaredField.setAccessible(true);
            // 从用户传入的参数如 User 中获取对应的属性值
            Object o = declaredField.get(params[0]);

            // 设置参数,参数索引从1开始
            preparedStatement.setObject(i+1, o);
        }
        return preparedStatement;
    }

    // 根据类的全路径获取字节码对象
    private Class<?> getClassType(String paramterType) throws ClassNotFoundException {
        if (paramterType!=null) {
            Class<?> aClass = Class.forName(paramterType);
            return aClass;
        }

        return null;
    }

    // 解析sql #{} 为 ? 并存储参数
    private BoundSql getBoundSql(String sql) {
        // 1.创建TokenHandler标记处理器 配合 GenericTokenParse通用标记解析器完成配置文件等的解析工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        // 2.创建GenericTokenParser通用标记解析器完成对sql代码的解析
        // TokenHandler完成对表达式的处理,存储表达式#{}中的参数
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);

        // 解析sql
        String sqlText = genericTokenParser.parse(sql);
        // 存储占位符中的参数
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        // 将解析结果封装到BoundSql中
        BoundSql boundSql = new BoundSql(sqlText, parameterMappings);
        return boundSql;
    }

}
